import datetime

from django.db import transaction
from django.db.models import F, Case, When, Sum
from django.db.models.functions import Coalesce
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer

from base.api.utils import update_customer_member_points, update_customer_active_points
from finance.api.utils import increase_account_amount
from finance.models import Prepayment
from sale.models import SaleOrderDetail, SaleOrder, SaleReceiptDetail, SaleReceipt
from store.api.utils import sale_increase_goods_stock, sale_decrease_goods_stock
from store.models import GoodsStock


class SaleOrderCustomerSerializer(ModelSerializer):
    """
    借出单客户列表
    """
    customer_name = serializers.CharField(source='customer.name', read_only=True)

    class Meta:
        model = SaleOrder
        fields = ["customer", "customer_name"]


class SaleOrderListSerializer(ModelSerializer):
    """
    销售单
    """
    customer_name = serializers.CharField(source='customer.name', read_only=True)
    saler_name = serializers.CharField(source='saler.real_name', read_only=True)
    oper_user_name = serializers.CharField(source='oper_user.real_name', read_only=True)
    house_name = serializers.CharField(source='house.name', read_only=True)
    account_name = serializers.CharField(source='account.name', read_only=True)
    status_name = serializers.CharField(source='get_status_display', read_only=True)
    flag_name = serializers.CharField(source='get_flag_display', read_only=True)
    norecv_amount = serializers.SerializerMethodField()
    category = serializers.SerializerMethodField()

    class Meta:
        model = SaleOrder
        exclude = ["update_time", "add_time"]

    def get_category(self, obj):
        return 0

    def get_norecv_amount(self, obj):
        total_amount = obj.total_amount or 0
        amount = obj.amount or 0
        return total_amount - amount


class SaleOrderDetailSerializer(ModelSerializer):
    """
    销售出库单明细
    """
    id = serializers.IntegerField(required=False)
    goods_code = serializers.CharField(source='goods.code', read_only=True)
    goods_name = serializers.CharField(source='goods.name', read_only=True)
    goods_cate_name = serializers.CharField(source='goods.cate.name', read_only=True)
    goods_specs = serializers.CharField(source='goods.specs', read_only=True)
    goods_unit_name = serializers.CharField(source='goods.unit.name', read_only=True)
    sale_order_code = serializers.CharField(source='sale_order.code', read_only=True)
    sale_order_time = serializers.CharField(source='sale_order.order_time', read_only=True)
    sale_order_house = serializers.CharField(source='sale_order.house.name', read_only=True)
    customer_name = serializers.CharField(source='sale_order.customer.name', read_only=True)
    stock_nums = serializers.SerializerMethodField()
    # lend_nums = serializers.CharField(source='nums', read_only=True)

    class Meta:
        model = SaleOrderDetail
        exclude = ["update_time", "add_time"]

    def get_stock_nums(self, obj):
        results = obj.goods.stocks.all().aggregate(goods_nums=Sum("quantity"))
        if results:
            return results.get("goods_nums")
        return 0


class SaleOrderSerializer(ModelSerializer):
    """
    销售单
    """
    customer_name = serializers.CharField(source='customer.name', read_only=True)
    saler_name = serializers.CharField(source='saler.real_name', read_only=True)
    oper_user_name = serializers.CharField(source='oper_user.real_name', read_only=True)
    house_name = serializers.CharField(source='house.name', read_only=True)
    account_name = serializers.CharField(source='account.name', read_only=True)
    status_name = serializers.CharField(source='get_status_display', read_only=True)
    flag_name = serializers.CharField(source='get_flag_display', read_only=True)
    norecv_amount = serializers.SerializerMethodField()
    details = SaleOrderDetailSerializer(many=True)
    return_status = serializers.SerializerMethodField()
    total_nums = serializers.SerializerMethodField()
    total_return_nums = serializers.SerializerMethodField()
    category = serializers.SerializerMethodField()

    class Meta:
        model = SaleOrder
        exclude = ["update_time", "add_time"]

    def get_category(self, obj):
        return 0

    def get_norecv_amount(self, obj):
        total_amount = obj.total_amount or 0
        amount = obj.amount or 0
        return total_amount - amount

    def get_total_nums(self, obj):
        if obj.flag == 3:
            results = obj.details.all().aggregate(total_nums=Sum("nums"))
            if results:
                return results.get("total_nums") or 0
        return 0

    def get_total_return_nums(self, obj):
        if obj.flag == 3:
            results = obj.details.all().aggregate(total_return_nums=Sum("return_nums"))
            if results:
                return results.get("total_return_nums") or 0
        return 0

    def get_return_status(self, obj):
        if obj.flag == 3:
            results = obj.details.all().aggregate(total_nums=Sum("nums"), total_return_nums=Sum("return_nums"))
            # print(results)
            if results:
                total_nums = results.get("total_nums") or 0
                total_return_nums = results.get("total_return_nums") or 0
                if total_nums <= total_return_nums:
                    print(obj.status)
                    if obj.status != 3:
                        obj.status = 3  # 已经归还

                        obj.save(update_fields=['status'])
                    return '已归还'
                else:
                    if obj.status != 2:
                        obj.status = 2  # 未归还
                        obj.save(update_fields=['status'])
                    return '未归还'
        return ""

    def create(self, validated_data):
        details = validated_data.pop('details')
        instance = None
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                flag = validated_data.get('flag', 0)
                imprest = validated_data.get('imprest', False)
                if flag == 1:   # 销售退款
                    validated_data["total_amount"] *= -1
                    validated_data["amount"] *= -1
                    if imprest:
                        validated_data["imp_amount"] *= -1
                    for item in details:
                        if item.get("nums") > 0:
                            item["nums"] = -1 * item.get("nums")
                        if item.get("total_amount") > 0:
                            item["total_amount"] = -1 * item.get("total_amount")

                order_time = validated_data.get("order_time", None)
                if not order_time:
                    validated_data["order_time"] = datetime.datetime.now().date()
                instance = self.Meta.model.objects.create(**validated_data)
                # 直接收款
                if imprest:
                    account = validated_data.get("account")
                    imp_amount = validated_data.get("imp_amount")
                    increase_account_amount(account.id, imp_amount, instance.customer_id)

                for item in details:
                    item["batch_num"] = None if item["batch_num"] == "" else item["batch_num"]

                create_list = [SaleOrderDetail(sale_order=instance, **d) for d in details]
                objs = SaleOrderDetail.objects.bulk_create(create_list)
                # 更新库存
                for obj in objs:
                    self.update_goods_stock(instance.flag, obj)

            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)
            transaction.savepoint_commit(save_id)
        return instance

    def update(self, instance, validated_data):
        lists = validated_data.pop('details')

        with transaction.atomic():
            # 创建事务保存点
            save_id = transaction.savepoint()
            try:
                instance.code = validated_data.get('code', instance.code)
                instance.order_time = validated_data.get('order_time', instance.order_time)

                old_customer_id = instance.customer.id
                instance.customer = validated_data.get('customer', instance.customer)
                instance.saler = validated_data.get('saler', instance.saler)

                old_house_id = instance.house.id
                instance.house = validated_data.get('house', instance.house)

                instance.total_amount = validated_data.get('total_amount', instance.total_amount)
                instance.amount = validated_data.get('amount', instance.amount)
                instance.status = validated_data.get('status', instance.status)
                instance.flag = validated_data.get('flag', instance.flag)

                old_imprest = instance.imprest
                imprest = validated_data.get('imprest', False)

                old_account = instance.account
                account = validated_data.get('account', None)

                old_imp_amount = instance.imp_amount
                imp_amount = validated_data.get('imp_amount', 0)

                instance.imprest = validated_data.get('imprest', instance.imprest)
                instance.account = validated_data.get('account', instance.account)
                instance.imp_amount = validated_data.get('imp_amount', instance.imp_amount)

                instance.address = validated_data.get('address', instance.address)
                instance.remark = validated_data.get('remark', instance.remark)
                instance.oper_user = validated_data.get('oper_user', instance.oper_user)

                if instance.flag == 1:      # 销售退货
                    instance.total_amount = instance.total_amount * (-1) if instance.total_amount > 0 else instance.total_amount
                    instance.amount = instance.amount * (-1) if instance.amount > 0 else instance.amount
                    if imprest:
                        instance.imp_amount = instance.imp_amount * (-1) if instance.imp_amount > 0 else instance.imp_amount

                instance.save()

                if old_imprest and imprest:  # 修改
                    print(old_imp_amount, imp_amount)
                    increase_account_amount(old_account.id, -1 * old_imp_amount, old_customer_id)
                    increase_account_amount(account.id, instance.imp_amount, instance.customer_id)
                else:
                    if imprest:
                        increase_account_amount(account.id, instance.imp_amount, instance.customer_id)
                    elif old_imprest:
                        increase_account_amount(old_account.id, -1 * old_imp_amount, old_customer_id)

                for l in lists:
                    _id = l.pop("id", None)
                    l["batch_num"] = None if l["batch_num"] == "" else l["batch_num"]

                    if instance.flag == 1:      # 销售退货
                        if l["nums"] > 0:
                            l["nums"] *= -1
                        if l["total_amount"] > 0:
                            l["total_amount"] *= -1

                    if _id:
                        org_obj = SaleOrderDetail.objects.select_related('sale_order').filter(id=_id).first()
                        org_obj.old_house_id = old_house_id
                        SaleOrderDetail.objects.filter(id=_id).update(**l)
                        obj = SaleOrderDetail.objects.filter(id=_id).first()
                    else:
                        org_obj = None
                        obj = SaleOrderDetail.objects.create(sale_order=instance, **l)
                    # 更新库存
                    self.update_goods_stock(instance.flag, obj, org_obj)
            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)

            transaction.savepoint_commit(save_id)

        return instance

    def update_goods_stock(self, flag, obj, org_obj=None):
        if flag == 0 or flag == 2 or flag == 3:  # 销售出库/其他出库/借出登记
            if org_obj:
                sale_increase_goods_stock(org_obj)
                # 减少新库存
                sale_decrease_goods_stock(obj)
            else:
                sale_decrease_goods_stock(obj)

        elif flag == 1:     # 销售退货
            stocks = GoodsStock.objects.filter(goods_id=obj.goods_id, house_id=obj.sale_order.house_id)
            nums = abs(obj.nums)
            old_nums = 0
            if org_obj:
                old_nums = abs(org_obj.nums)

            diff_nums = nums - old_nums
            for stock in stocks:
                if diff_nums >= 0 or stock.quantity > abs(diff_nums):
                    stock.quantity = F('quantity') + diff_nums
                    stock.save(update_fields=['quantity'])
                    break
                else:
                    stock.quantity = 0
                    stock.price = 0
                    diff_nums += stock.quantity
                    stock.save(update_fields=['quantity', 'price'])


class SaleReceiptDetailSerializer(ModelSerializer):
    """
    销售收款单明细
    """
    id = serializers.IntegerField(required=False)   # 修改需要，默认不带id值
    receipt_order_code = serializers.CharField(source='receipt_order.code', read_only=True)
    receipt_order_time = serializers.CharField(source='receipt_order.recv_time', read_only=True)
    receipt_order_account_name = serializers.CharField(source='receipt_order.account.name', read_only=True)
    sale_order_code = serializers.CharField(source='sale_order.code', read_only=True)
    sale_order_time = serializers.CharField(source='sale_order.order_time', read_only=True)
    sale_order_total_amount = serializers.CharField(source='sale_order.total_amount', read_only=True)
    sale_order_amount = serializers.CharField(source='sale_order.amount', read_only=True)
    flag_name = serializers.CharField(source='receipt_order.get_flag_display', read_only=True)
    prepay_total_amount = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = SaleReceiptDetail
        exclude = ["update_time", "add_time"]

    def get_prepay_total_amount(self, obj):
        if obj.category == 1:
            result = Prepayment.objects.filter(customer_id=obj.receipt_order.customer_id)\
                .aggregate(total_amount=Sum(Coalesce(F('amount'), 0.0) - Coalesce(F('deduct_amount'), 0.0)))
            return result.get("total_amount") * -1
        return 0


class SaleReceiptSerializer(ModelSerializer):
    """
    销售收款单
    """
    account_name = serializers.CharField(source='account.name', read_only=True)
    customer_name = serializers.CharField(source='customer.name', read_only=True)
    user_name = serializers.CharField(source='user.real_name', read_only=True)
    flag_name = serializers.CharField(source='get_flag_display', read_only=True)
    oper_name = serializers.CharField(source='oper.real_name', read_only=True)
    details = SaleReceiptDetailSerializer(many=True)

    class Meta:
        model = SaleReceipt
        exclude = ["update_time", "add_time"]

    def create(self, validated_data):
        details = validated_data.pop('details')
        instance = None
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                recv_time = validated_data.get("recv_time", None)
                if not recv_time:
                    validated_data["recv_time"] = datetime.datetime.now().date()

                instance = self.Meta.model.objects.create(**validated_data)
                create_list = [SaleReceiptDetail(receipt_order=instance, **d) for d in details]
                objs = SaleReceiptDetail.objects.bulk_create(create_list)
                # 增加账户金额
                self.update_account(instance, True)     # 创建
                # 减少库存
                for obj in objs:
                    if obj.category >= 1:
                        self.prepay_amount(obj)
                    self.update_sale_order(obj)

            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)
            transaction.savepoint_commit(save_id)
        return instance

    def update(self, instance, validated_data):
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                details = validated_data.pop('details')
                instance.code = validated_data.get('code', instance.code)
                old_account_id = instance.account_id
                instance.recv_time = validated_data.get('recv_time', instance.recv_time)

                old_customer_id = instance.customer.id
                instance.customer = validated_data.get('customer', instance.customer)

                instance.user = validated_data.get('user', instance.user)
                instance.receivable = validated_data.get('receivable', instance.receivable)
                instance.amount = validated_data.get('amount', instance.amount)
                instance.discount_amount = validated_data.get('discount_amount', instance.discount_amount)

                old_actual_amount = instance.actual_amount
                instance.actual_amount = validated_data.get('actual_amount', instance.actual_amount)
                instance.oper = validated_data.get('oper', instance.oper)
                instance.remark = validated_data.get('remark', instance.remark)
                instance.save()
                # 修改账户余额
                instance.old_account_id = old_account_id
                instance.old_actual_amount = old_actual_amount
                instance.old_customer_id = old_customer_id
                self.update_account(instance, False)

                for d in details:
                    detail_id = d.pop("id", None)
                    if detail_id:
                        org_obj = SaleReceiptDetail.objects.filter(id=detail_id).first()
                        SaleReceiptDetail.objects.filter(id=detail_id).update(**d)
                        obj = SaleReceiptDetail.objects.filter(id=detail_id).first()
                    else:
                        org_obj = None
                        obj = SaleReceiptDetail.objects.create(receipt_order=instance, **d)

                    if d["category"] >= 1:
                        self.prepay_amount(obj, org_obj)
                    # 更新销售出库表收款数据
                    self.update_sale_order(obj, org_obj)
            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)
            transaction.savepoint_commit(save_id)

        return instance

    def update_sale_order(self, obj, org_obj=None):
        if org_obj is None:
            SaleOrder.objects.filter(id=obj.sale_order_id)\
                .update(
                        amount=F("amount") + obj.amount,
                        status=Case(When(total_amount__lte=F("amount"), then=1), default=0),
                        order_status=1
                        )
        else:
            SaleOrder.objects.filter(id=obj.sale_order_id)\
                .update(
                        amount=F("amount") + obj.amount - org_obj.amount,
                        status=Case(When(total_amount__lte=F("amount"), then=1), default=0)
                        )

    def update_account(self, instance, created):
        """更新账户金额"""
        if created:    # 创建
            increase_account_amount(instance.account_id, instance.actual_amount, instance.customer_id)
        else:
            increase_account_amount(instance.old_account_id, -instance.old_actual_amount, instance.old_customer_id)
            increase_account_amount(instance.account_id, instance.actual_amount, instance.customer_id)

    def prepay_amount(self, obj, org_obj=None):
        if obj.category == 1:   # 预收款
            data = {
                "order_time": obj.receipt_order.recv_time,
                "deduct_amount": abs(obj.amount),
                "flag": 1,
                "remark": obj.remark,
                "account_id": obj.receipt_order.account_id,
                "customer_id": obj.receipt_order.customer_id,
                "user_id": obj.receipt_order.user_id,
                "oper_user_id": obj.receipt_order.oper_id,

            }
            code = obj.receipt_order.code + '预收款'
            Prepayment.objects.update_or_create(code=code, defaults=data)

        elif obj.category == 2:     # 会员积分
            if org_obj:
                update_customer_member_points(org_obj.receipt_order.customer_id, -1 * org_obj.amount)

            update_customer_member_points(obj.receipt_order.customer_id, obj.amount)

        elif obj.category == 3:     # 活动积分
            if org_obj:
                update_customer_active_points(org_obj.receipt_order.customer_id, -1 * org_obj.amount)

            update_customer_active_points(obj.receipt_order.customer_id, obj.amount)
        else:
            pass
